package com.itaming.lycheeframework.security.authentication;

import com.itaming.lycheeframework.security.exception.AuthenticationException;
import com.itaming.lycheeframework.security.token.TokenService;
import com.itaming.lycheeframework.support.utils.ClassUtil;
import com.itaming.lycheeframework.support.web.OncePerRequestInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpMethod;
import org.springframework.web.method.HandlerMethod;

/**
 * Token 认证拦截器
 *
 * @author A.Ming
 */
@RequiredArgsConstructor
public class TokenInterceptor extends OncePerRequestInterceptor {

    /**
     * Token提供者
     */
    private final TokenService tokenService;

    /**
     * 认证上下文提供者
     */
    private final AuthContextProvider<?, ?> authContextProvider;

    /**
     * 预处理
     *
     * @param request  请求
     * @param response 响应
     * @param handler  处理器
     * @return boolean
     * @throws AuthenticationException 认证异常，Token验证失败时抛出
     */
    @Override
    protected boolean doPreHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws AuthenticationException {
        // 判断是否为HandlerMethod
        // HandlerMethod代表的是通过@RequestMapping注解映射的控制器方法
        // 不是HandlerMethod的情况可能是：
        // 1. 静态资源请求：处理器是ResourceHttpRequestHandler
        // 2. WebSocket请求：处理器是WebSocketHandler
        // 3. 自定义处理器
        if (!(handler instanceof HandlerMethod handlerMethod)) {
            return true;
        }

        // 放行OPTIONS请求
        if (HttpMethod.OPTIONS.name().equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }

        // 检查是否允许匿名访问
        boolean allowAnonymous = ClassUtil.isAnnotated(handlerMethod, Anonymous.class);
        if (allowAnonymous) {
            return true;
        }

        // 验证token，获取标识符
        // 验证失败则抛出异常
        Object id = tokenService.getId(request);

        // 设置上下文
        AuthContextHolder.setContext(AuthContext.of(id, authContextProvider));

        // 默认放行
        return true;
    }

    /**
     * 请求完成后的处理
     *
     * @param request  请求
     * @param response 响应
     * @param handler  处理器
     * @param ex       请求处理过程中抛出的异常
     */
    @Override
    protected void doAfterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 清理上下文
        AuthContextHolder.clear();
    }

}
